package com.anlogic.sdk.app.handlers;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.eclipse.cdt.core.templateengine.process.ProcessFailureException;
import org.eclipse.cdt.managedbuilder.core.BuildException;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;

import com.anlogic.sdk.openocd.OpenocdLaunchConfiguration;
import com.anlogic.sdk.project.settings.ProjectSettings;
import com.anlogic.sdk.project.settings.utils.riscv.SetBooleanOptionByID;
import com.anlogic.sdk.project.settings.utils.riscv.SetStringListOptionByID;
import com.anlogic.sdk.project.settings.utils.riscv.SetStringOptionByID;
import com.anlogic.sdk.toolchain.RiscvToolchain;
import com.anlogic.sdk.tools.IBspTool;
import com.anlogic.sdk.utils.FileUtils;
import com.anlogic.sdk.utils.ProjectUtils;
import com.anlogic.sdk.utils.options.ChipType;
import com.anlogic.sdk.utils.options.OsType;
import com.anlogic.sdk.utils.toolchain.ToolchainUtils;

public class RiscvProjectHandler implements IProjectHandler {
	public RiscvProjectHandler() {
	}
	
	public IConfiguration[] createConfigurations() {
		IConfiguration[] configs = new IConfiguration[2];
		configs[0] = ManagedBuildManager.getExtensionConfiguration("ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.debug");
		configs[1] = ManagedBuildManager.getExtensionConfiguration("ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release");
		return configs;
	}
	
	public IConfiguration[] getConfigurations(IProject project) {
		return ManagedBuildManager.getBuildInfo((IResource) project).getManagedProject().getConfigurations();
	}
	
	public void setProjectSettings(IProject project, String projectType, String os, String arch) {
		ProjectSettings projSettings = new ProjectSettings(project);
		projSettings.createNewSettings();
		projSettings.setProjectType(projectType);
		projSettings.setArch(arch);
		projSettings.setOs(os);
		projSettings.saveProjectSettings();
	}
	
	public void setProjectToolchain(IProject project, String projectType, String os, String arch) {
		String toolchainName = ToolchainUtils.getToolchainName(os, arch, projectType);
		String toolchainPath = ToolchainUtils.getToolchainPath(os, arch, projectType);
		String buildToolsPath = ToolchainUtils.getBuildToolsPath();
		RiscvToolchain.setToolchain(project, toolchainName, toolchainPath, buildToolsPath);
	}
	
	public void setLaunchConfiguration(IProject project, IProject platProj, String projectType, String os, String arch, String chipName, String procName) {
		if (OsType.isLinux(os)) {
			return;
		}
		
		OpenocdLaunchConfiguration launchConfig = OpenocdLaunchConfiguration.newConfig(project.getName());
		
		launchConfig.setRunPlatformInit(ToolchainUtils.getRunPlatInit(chipName));
		launchConfig.setGdbServerOtherOptions(ToolchainUtils.getGdbServerOtherOptions(arch, projectType));
		launchConfig.setGdbServerExecutable(ToolchainUtils.getOpenOCDExecutable());
		launchConfig.setGdbClientExecutable(ToolchainUtils.getGDBExecutable(os, arch, projectType));
		launchConfig.setGdbClientOtherCommands(ToolchainUtils.getGdbClientCommands(arch, projectType));
		launchConfig.setGdbServerPort(Integer.parseInt("3333"));
		launchConfig.setRemotePort(Integer.parseInt(ToolchainUtils.getGdbServerPort(chipName, procName)));
		launchConfig.setGdbCliPort(Integer.parseInt(ToolchainUtils.getGdbCliPort(chipName, procName)));
		launchConfig.setDoFirstReset(false);
		launchConfig.setDoSecondReset(false);
		launchConfig.setFirstResetType("");
		launchConfig.setSecondResetType("");
		
		File[] bitFiles = FileUtils.getFilesBySuffix(platProj.getLocation().toString() + "/platform/", ".bit");
		if (bitFiles != null && bitFiles.length > 0) {
			launchConfig.setBitstreamFile("${bsp_loc}/platform/" + bitFiles[0].getName());
			launchConfig.setDoProgramFPGA(true);
		} else {
			launchConfig.setBitstreamFile("");
			launchConfig.setDoProgramFPGA(false);
		}
		
		launchConfig.doSave();
	}
	
	public void setToolchainOptions(IBspTool bspTool, IProject project, IProject platProj, String appName, String chipName, String os, String arch, IConfiguration config) throws BuildException, ProcessFailureException {
		if (OsType.isLinux(os)) {
			return;
		}
		
		String projectName = project.getName();
		String projLoc = project.getLocation().toString();
		String bspLoc = platProj.getLocation().toString();
		
		List<String> projIncludes = bspTool.getIncludePaths(projLoc);
		List<String> bspIncludes = bspTool.getIncludePaths(bspLoc);
		String[] includePaths = new String[projIncludes.size() + bspIncludes.size()];
		
		for (int i = 0; i < projIncludes.size(); i++) {
			includePaths[i] = String.format("\"%s\"", projIncludes.get(i).replace(projLoc, "${ProjDirPath}"));
		}
		
		for (int i = 0; i < bspIncludes.size(); i++) {
			includePaths[i + projIncludes.size()] = String.format("\"%s\"", bspIncludes.get(i).replace(bspLoc, "${bsp_loc}"));
		}
		
		new SetStringListOptionByID(projectName, config.getName()).setCompilerIncludePaths(includePaths);
		
		if (!config.isManagedBuildOn() || !ProjectUtils.isApplicationProject(project)) {
			return;
		}
		
		Map<String, String> buildProperties = bspTool.getAppBuildProperties(appName);
		List<String> InvisibleDefs = new ArrayList<String>();
		List<String> libList = new ArrayList<String>() ;
		String download = "ddr";
		
		if (buildProperties.containsKey("DOWNLOAD")) {
			download = buildProperties.get("DOWNLOAD");
		}
		
		if (appName.toLowerCase().contains("fsbl")) {
			download = "ocm";
			InvisibleDefs.add("DOWNLOAD=ocm");
			InvisibleDefs.add("ARMv8_EL=EL3");
			InvisibleDefs.add("DDR_2M_MAPPING=0");
			buildProperties.remove("DOWNLOAD");
			buildProperties.remove("ARMv8_EL");
			buildProperties.remove("DDR_2M_MAPPING");
			libList.add("fatfs");
			libList.add("cherryusb_driver");
		}
		libList.add("plat");
		if (ChipType.isDr1v90(chipName)) {
			libList.add("ddr");
		}
		libList.add("stdc++");
		libList.add("gcc");
		libList.add("c");
		libList.add("m");
		libList.add("g");
		libList.add("albsp");
		
		if (!OsType.isStandalone(os)) {
			libList.add(os.toLowerCase());
		}
		
		List<String> buildDefs = new ArrayList<String>();
		for (Map.Entry<String, String> entry : buildProperties.entrySet()) {
			if (entry.getValue().isEmpty()) {
				buildDefs.add(entry.getKey());
			} else {
				buildDefs.add(String.format("%s=%s", entry.getKey(), entry.getValue()));
			}
		}
		
		List<String> linkScripts = new ArrayList<String>();
		if (ChipType.isDr1v90(chipName)) {
			linkScripts.add(String.format("${ProjDirPath}/lds/gcc_%s_%s_%s.ld", chipName, download, "lp64d"));
		} else {
			linkScripts.add(String.format("${ProjDirPath}/lds/gcc_%s_%s.ld", chipName, download));
		}
		
		List<String> linkPaths = new ArrayList<String>();
		linkPaths.add("${ProjDirPath}/Debug/bsp_build/lib");
		if (ChipType.isDr1v90(chipName)) {
			linkPaths.add("\"${bsp_loc}/prebuild/dr1v90\"");
		}
		
		new SetStringListOptionByID(projectName, config.getName()).setLinkerScriptfile(linkScripts.toArray(new String[0]));
		if (ChipType.isDr1v90(chipName)) {
			new SetStringOptionByID(projectName, config.getName()).setARCHbase("rv64i");
			new SetStringOptionByID(projectName, config.getName()).setARCHfp("fd");
			new SetStringOptionByID(projectName, config.getName()).setABIbase("lp64");
			new SetStringOptionByID(projectName, config.getName()).setABIfp("d");
		} else if (ChipType.isPh1p35(chipName)) {
			new SetStringOptionByID(projectName, config.getName()).setARCHbase("rv32i");
			new SetStringOptionByID(projectName, config.getName()).setARCHfp("fd");
			new SetStringOptionByID(projectName, config.getName()).setABIbase("ilp32");
		}
		new SetBooleanOptionByID(projectName, config.getName()).setARCHmultiply("true");
		new SetBooleanOptionByID(projectName, config.getName()).setARCHatomic("true");
		new SetBooleanOptionByID(projectName, config.getName()).setARCHcompressed("true");
		new SetBooleanOptionByID(projectName, config.getName()).setARCHdiv("false");
		new SetStringOptionByID(projectName, config.getName()).setOptimizationLevel("none");
		new SetStringOptionByID(projectName, config.getName()).setDebugggingLevel("default");
		new SetStringOptionByID(projectName, config.getName()).setCodeModel("any");                                     
		
		new SetStringListOptionByID(projectName, config.getName()).setCompilerDefs(buildDefs.toArray(new String[0]));
		new SetStringListOptionByID(projectName, config.getName()).setInvisibleCompilerDefs(InvisibleDefs.toArray(new String[0]));
		new SetStringListOptionByID(projectName, config.getName()).setLinkerLibs(libList.toArray(new String[0]));
		new SetStringListOptionByID(projectName, config.getName()).setLinkerPaths(linkPaths.toArray(new String[0]));

		new SetBooleanOptionByID(projectName, config.getName()).setMessageLength("false");
		new SetBooleanOptionByID(projectName, config.getName()).setSignedChar("false");
		new SetBooleanOptionByID(projectName, config.getName()).setFunctionSections("true");
		new SetBooleanOptionByID(projectName, config.getName()).setDataSections("true");
		new SetBooleanOptionByID(projectName, config.getName()).setNoCommon("true");
		new SetBooleanOptionByID(projectName, config.getName()).setFreedstanding("true");
		new SetBooleanOptionByID(projectName, config.getName()).setNoBuiltin("true");
		new SetBooleanOptionByID(projectName, config.getName()).setNoStart("true");
		new SetBooleanOptionByID(projectName, config.getName()).setUseNewlibNano("true");
		new SetBooleanOptionByID(projectName, config.getName()).setUseNewlibNosys("true");
	}
	
	public void updatePreBuildStep(IConfiguration config) {
		RiscvToolchain.updatePreBuildStep(config);
	}
}
